/*=====================================================================

  File:      Triangle.cs
  Summary:   A SQL Server user-defined type that represents a triangle in space
  Date:	     May 27, 2006

---------------------------------------------------------------------

  This file is part of the Microsoft SQL Server Code Samples.
  Copyright (C) Microsoft Corporation.  All rights reserved.

This source code is intended only as a supplement to Microsoft
Development Tools and/or on-line documentation.  See these other
materials for detailed information regarding Microsoft code samples.

THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

======================================================= */

using System;
using System.Data.Sql;
using System.Data.SqlTypes;
using System.Diagnostics.CodeAnalysis;
using Microsoft.SqlServer.Server;

namespace Microsoft.Samples.SqlServer.DB3D
{
    // Triangle is a SQL Server user-defined type that represents a flat triangle
    // in a 3-dimentional space. The class has state to represent the triangle
    // and a few primitives to manipulate it (e.g. move it (translate), rotate it,
    // change its size (scale)).

	[SqlUserDefinedType(Format.Native)]
    [Serializable]
    [SuppressMessage("Microsoft.Performance", "CA1815", 
                     Justification = "The value types used in this example are not meant to be compared directly.")]
    public struct Triangle: INullable
    {
		Point3D _p1;
		Point3D _p2;
		Point3D _p3;
		bool _isnull;

		// THE null instance
		// note that this can be done only if the null instance is inmutable
		// otherwise, it's better to just hand out a new instance in "null state"
		// every time the Null propery is accessed
		static readonly Triangle NULL = new Triangle(true);

		private Triangle(bool setnull) {
			_p1 = new Point3D(0, 0, 0);
            _p2 = new Point3D(0, 0, 0);
            _p3 = new Point3D(0, 0, 0);
			_isnull = setnull;
		}

		public Triangle(Point3D p1, Point3D p2, Point3D p3) {
			_p1 = p1;
			_p2 = p2;
			_p3 = p3;
			_isnull = false;
		}

		public Point3D P1 {
			get { return _p1; }
			set { _p1 = value; }
		}

		public Point3D P2 {
			get { return _p2; }
			set { _p2 = value; }
		}

		public Point3D P3 {
			get { return _p3; }
			set { _p3 = value; }
		}

        [SqlMethod(IsMutator = true, OnNullCall = false)]
		public void LocalRotate(double anglex, double angley, double anglez, Point3D center) {
			Translate(-center.X, -center.Y, -center.Z);
			Rotate(anglex, angley, anglez);
			Translate(center.X, center.Y, center.Z);
		}

        [SqlMethod(IsMutator = true, OnNullCall = false)]
        public void Rotate(double anglex, double angley, double anglez) {
			RotateX(anglex);
			RotateY(angley);
			RotateZ(anglez);
		}

        [SqlMethod(IsMutator = true, OnNullCall = false)]
        public void RotateX(double angle) {
			_p1.RotateX(angle);
			_p2.RotateX(angle);
			_p3.RotateX(angle);
		}

        [SqlMethod(IsMutator = true, OnNullCall = false)]
        public void RotateY(double angle) {
			_p1.RotateY(angle);
			_p2.RotateY(angle);
			_p3.RotateY(angle);
		}

        [SqlMethod(IsMutator = true, OnNullCall = false)]
        public void RotateZ(double angle) {
			_p1.RotateZ(angle);
			_p2.RotateZ(angle);
			_p3.RotateZ(angle);
		}

        [SqlMethod(IsMutator = true, OnNullCall = false)]
        public void Translate(double x, double y, double z) {
			_p1.Translate(x, y, z);
			_p2.Translate(x, y, z);
			_p3.Translate(x, y, z);
		}

        [SqlMethod(IsMutator = true, OnNullCall = false)]
        public void Scale(double s) {
			_p1.Scale(s);
			_p2.Scale(s);
			_p3.Scale(s);
		}

		public override string ToString() {
			return _isnull ? "<<null>>" : _p1.ToString() + ";" + _p2.ToString() + ";" + _p3.ToString();
		}

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", 
            "CA1303:DoNotPassLiteralsAsLocalizedParameters", 
            MessageId = "System.ArgumentException.#ctor(System.String,System.String)",
            Justification = "Trying to keep the sample simple for clarity")]
        public static Triangle Parse(SqlString s) {
			if("<<null>>" == s.Value)
				return Null;

			string[] values = s.Value.Split(';');
			if(3 != values.Length)
				throw new ArgumentException("Invalid format for triangle, should be x1,y1,z1;x2,y2,z2;x3,y3,z3", "s");

			// Point3D will throw if the portion of the string is in invalid format for a point
            return new Triangle(Point3D.Parse(values[0]),
			                    Point3D.Parse(values[1]),
			                    Point3D.Parse(values[2]));
		}

		public static Triangle Null {
			get { return NULL; }
		}

        public bool IsNull {
            get { return _isnull; }
        }
    }
}
